use crate::config::app_error::AppResult;
use crate::extension::index::index::indexer::Indexer;
use crate::extension::index::index::key_comparator;
use crate::extension::index::index::query::index_attribute::IndexAttributeFactory;
use crate::extension::index::index::query::index_descriptor::IndexDescriptor;
use crate::extension::index::index::query::index_entry::{IndexEntry, IndexEntryImpl};
use crate::extension::index::index::query::index_spec::{IndexSpec, OrderType, PRIMARY_INDEX_NAME};
use crate::extension::index::index::query::query::QueryIndexView;
use halo_model::{ExtensionGVK, ExtensionOperator, GVK};
use macros::gvk;
use ordermap::{OrderMap, OrderSet};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use std::sync::Arc;

/**
 * Create a {@link QueryIndexView} for employee to test.
 * <pre>
 * | id | firstName | lastName | email | hireDate | salary | managerId | departmentId |
 * |----|-----------|----------|-------|----------|--------|-----------|--------------|
 * | 100| Pat       | Fay      | p     | 17       | 2600   | 101       | 50           |
 * | 101| Lee       | Day      | l     | 17       | 2400   | 102       | 40           |
 * | 102| William   | Jay      | w     | 19       | 2200   | 102       | 50           |
 * | 103| Mary      | Day      | p     | 17       | 2000   | 103       | 50           |
 * | 104| John      | Fay      | j     | 17       | 1800   | 103       | 50           |
 * | 105| Gon       | Fay      | p     | 18       | 1900   | 101       | 40           |
 * </pre>
 *
 * @return a {@link QueryIndexView} for employee to test
 */
pub fn create_employee_index_view() -> QueryIndexView<MockIndexer<FakeExtension>> {
    let id_entry = vec![
        ("100".to_string(), "100".to_string()),
        ("101".to_string(), "101".to_string()),
        ("102".to_string(), "102".to_string()),
        ("103".to_string(), "103".to_string()),
        ("104".to_string(), "104".to_string()),
        ("105".to_string(), "105".to_string()),
    ];
    let first_name_entry = vec![
        ("Pat".to_string(), "100".to_string()),
        ("Lee".to_string(), "101".to_string()),
        ("William".to_string(), "102".to_string()),
        ("Mary".to_string(), "103".to_string()),
        ("John".to_string(), "104".to_string()),
        ("Gon".to_string(), "105".to_string()),
    ];
    let last_name_entry = vec![
        ("Fay".to_string(), "100".to_string()),
        ("Day".to_string(), "101".to_string()),
        ("Jay".to_string(), "102".to_string()),
        ("Day".to_string(), "103".to_string()),
        ("Fay".to_string(), "104".to_string()),
        ("Fay".to_string(), "105".to_string()),
    ];
    let email_entry = vec![
        ("p".to_string(), "100".to_string()),
        ("l".to_string(), "101".to_string()),
        ("w".to_string(), "102".to_string()),
        ("p".to_string(), "103".to_string()),
        ("j".to_string(), "104".to_string()),
        ("p".to_string(), "105".to_string()),
    ];

    let hire_date_entry = vec![
        ("17".to_string(), "100".to_string()),
        ("17".to_string(), "101".to_string()),
        ("19".to_string(), "102".to_string()),
        ("17".to_string(), "103".to_string()),
        ("17".to_string(), "104".to_string()),
        ("18".to_string(), "105".to_string()),
    ];

    let salary_entry = vec![
        ("2600".to_string(), "100".to_string()),
        ("2400".to_string(), "101".to_string()),
        ("2200".to_string(), "102".to_string()),
        ("2000".to_string(), "103".to_string()),
        ("1800".to_string(), "104".to_string()),
        ("1900".to_string(), "105".to_string()),
    ];

    let manager_id_entry = vec![
        ("101".to_string(), "100".to_string()),
        ("102".to_string(), "101".to_string()),
        ("102".to_string(), "102".to_string()),
        ("103".to_string(), "103".to_string()),
        ("103".to_string(), "104".to_string()),
        ("101".to_string(), "105".to_string()),
    ];

    let department_id_entry = vec![
        ("50".to_string(), "100".to_string()),
        ("40".to_string(), "101".to_string()),
        ("50".to_string(), "102".to_string()),
        ("50".to_string(), "103".to_string()),
        ("50".to_string(), "104".to_string()),
        ("40".to_string(), "105".to_string()),
    ];

    let mut index_entrys = HashMap::new();
    pile_for_indexer(&mut index_entrys, PRIMARY_INDEX_NAME.to_string(), id_entry);
    pile_for_indexer(&mut index_entrys, "firstName".to_string(), first_name_entry);
    pile_for_indexer(&mut index_entrys, "lastName".to_string(), last_name_entry);
    pile_for_indexer(&mut index_entrys, "email".to_string(), email_entry);
    pile_for_indexer(&mut index_entrys, "hireDate".to_string(), hire_date_entry);
    pile_for_indexer(&mut index_entrys, "salary".to_string(), salary_entry);
    pile_for_indexer(&mut index_entrys, "managerId".to_string(), manager_id_entry);
    pile_for_indexer(
        &mut index_entrys,
        "departmentId".to_string(),
        department_id_entry,
    );
    let indexer = MockIndexer::mock_with(index_entrys);

    let view_impl = QueryIndexView::new::<FakeExtension>(Arc::new(RefCell::new(indexer))).unwrap();
    view_impl
}

/**
 * Create a {@link QueryIndexView} for post to test.
 * <pre>
 * | id  | title  | published | publishTime         | owner |
 * |-----|--------|-----------|---------------------|-------|
 * | 100 | title1 | true      | 2024-01-01T00:00:00 | jack  |
 * | 101 | title2 | true      | 2024-01-02T00:00:00 | rose  |
 * | 102 | title3 | false     | null                | smith |
 * | 103 | title4 | false     | null                | peter |
 * | 104 | title5 | false     | null                | john  |
 * | 105 | title6 | true      | 2024-01-05 00:00:00 | tom   |
 * | 106 | title7 | true      | 2024-01-05 13:00:00 | jerry |
 * | 107 | title8 | true      | 2024-01-05 12:00:00 | jerry |
 * | 108 | title9 | false     | null                | jerry |
 * </pre>
 *
 * @return a {@link QueryIndexView} for post to test
 */
pub fn create_post_index_view_with_null_cell() -> QueryIndexView<MockIndexer<FakeExtension>> {
    let id_entry = vec![
        ("100".to_string(), "100".to_string()),
        ("101".to_string(), "101".to_string()),
        ("102".to_string(), "102".to_string()),
        ("103".to_string(), "103".to_string()),
        ("104".to_string(), "104".to_string()),
        ("105".to_string(), "105".to_string()),
        ("106".to_string(), "106".to_string()),
        ("107".to_string(), "107".to_string()),
        ("108".to_string(), "108".to_string()),
    ];
    let title_entry = vec![
        ("title1".to_string(), "100".to_string()),
        ("title2".to_string(), "101".to_string()),
        ("title3".to_string(), "102".to_string()),
        ("title4".to_string(), "103".to_string()),
        ("title5".to_string(), "104".to_string()),
        ("title6".to_string(), "105".to_string()),
        ("title7".to_string(), "106".to_string()),
        ("title8".to_string(), "107".to_string()),
        ("title9".to_string(), "108".to_string()),
    ];
    let published_entry = vec![
        ("true".to_string(), "100".to_string()),
        ("true".to_string(), "101".to_string()),
        ("false".to_string(), "102".to_string()),
        ("false".to_string(), "103".to_string()),
        ("false".to_string(), "104".to_string()),
        ("true".to_string(), "105".to_string()),
        ("true".to_string(), "106".to_string()),
        ("true".to_string(), "107".to_string()),
        ("false".to_string(), "108".to_string()),
    ];
    let publish_time_entry = vec![
        ("2024-01-01T00:00:00".to_string(), "100".to_string()),
        ("2024-01-02T00:00:00".to_string(), "101".to_string()),
        ("2024-01-05 00:00:00".to_string(), "105".to_string()),
        ("2024-01-05 13:00:00".to_string(), "106".to_string()),
        ("2024-01-05 12:00:00".to_string(), "107".to_string()),
    ];

    let owner_entry = vec![
        ("jack".to_string(), "100".to_string()),
        ("rose".to_string(), "101".to_string()),
        ("smith".to_string(), "102".to_string()),
        ("peter".to_string(), "103".to_string()),
        ("john".to_string(), "104".to_string()),
        ("tom".to_string(), "105".to_string()),
        ("jerry".to_string(), "106".to_string()),
        ("jerry".to_string(), "107".to_string()),
        ("jerry".to_string(), "108".to_string()),
    ];

    let mut index_entrys = HashMap::new();
    pile_for_indexer(&mut index_entrys, PRIMARY_INDEX_NAME.to_string(), id_entry);
    pile_for_indexer(&mut index_entrys, "title".to_string(), title_entry);
    pile_for_indexer(&mut index_entrys, "published".to_string(), published_entry);
    pile_for_indexer(
        &mut index_entrys,
        "publishTime".to_string(),
        publish_time_entry,
    );
    pile_for_indexer(&mut index_entrys, "owner".to_string(), owner_entry);
    let indexer = MockIndexer::mock_with(index_entrys);

    let view_impl =
        QueryIndexView::new::<FakeExtension>(Arc::new(RefCell::new(indexer))).unwrap();
    view_impl
}

/**
 * Creates a fake comment index view for below data.
 * <pre>
 * | Name | Top   | Priority | Creation Time                    |
 * | ---- | ----- | -------- | -------------------------------- |
 * | 1    | True  | 0        | 2024-06-05 02:58:15.633165+00:00 |
 * | 2    | True  | 1        | 2024-06-05 02:58:16.633165+00:00 |
 * | 4    | True  | 2        | 2024-06-05 02:58:18.633165+00:00 |
 * | 3    | True  | 2        | 2024-06-05 02:58:17.633165+00:00 |
 * | 5    | True  | 3        | 2024-06-05 02:58:18.633165+00:00 |
 * | 6    | True  | 3        | 2024-06-05 02:58:18.633165+00:00 |
 * | 10   | False | 0        | 2024-06-05 02:58:17.633165+00:00 |
 * | 9    | False | 0        | 2024-06-05 02:58:17.633165+00:00 |
 * | 8    | False | 0        | 2024-06-05 02:58:16.633165+00:00 |
 * | 7    | False | 0        | 2024-06-05 02:58:15.633165+00:00 |
 * | 11   | False | 0        | 2024-06-05 02:58:14.633165+00:00 |
 * | 12   | False | 1        | 2024-06-05 02:58:14.633165+00:00 |
 * | 14   | False | 3        | 2024-06-05 02:58:17.633165+00:00 |
 * | 13   | False | 3        | 2024-06-05 02:58:14.633165+00:00 |
 * </pre>
 */
pub fn create_comment_index_view() -> QueryIndexView<MockIndexer<FakeExtension>> {
    let id_entry = vec![
        ("1".to_string(), "1".to_string()),
        ("2".to_string(), "2".to_string()),
        ("3".to_string(), "3".to_string()),
        ("4".to_string(), "4".to_string()),
        ("5".to_string(), "5".to_string()),
        ("6".to_string(), "6".to_string()),
        ("7".to_string(), "7".to_string()),
        ("8".to_string(), "8".to_string()),
        ("9".to_string(), "9".to_string()),
        ("10".to_string(), "10".to_string()),
        ("11".to_string(), "11".to_string()),
        ("12".to_string(), "12".to_string()),
        ("13".to_string(), "13".to_string()),
        ("14".to_string(), "14".to_string()),
    ];
    let creation_time_entry = vec![
        ("2024-06-05 02:58:15.633165".to_string(), "1".to_string()),
        ("2024-06-05 02:58:16.633165".to_string(), "2".to_string()),
        ("2024-06-05 02:58:17.633165".to_string(), "3".to_string()),
        ("2024-06-05 02:58:18.633165".to_string(), "4".to_string()),
        ("2024-06-05 02:58:18.633165".to_string(), "5".to_string()),
        ("2024-06-05 02:58:18.633165".to_string(), "6".to_string()),
        ("2024-06-05 02:58:15.633165".to_string(), "7".to_string()),
        ("2024-06-05 02:58:16.633165".to_string(), "8".to_string()),
        ("2024-06-05 02:58:17.633165".to_string(), "9".to_string()),
        ("2024-06-05 02:58:17.633165".to_string(), "10".to_string()),
        ("2024-06-05 02:58:14.633165".to_string(), "11".to_string()),
        ("2024-06-05 02:58:14.633165".to_string(), "12".to_string()),
        ("2024-06-05 02:58:14.633165".to_string(), "13".to_string()),
        ("2024-06-05 02:58:17.633165".to_string(), "14".to_string()),
    ];
    let top_entry = vec![
        ("true".to_string(), "1".to_string()),
        ("true".to_string(), "2".to_string()),
        ("true".to_string(), "3".to_string()),
        ("true".to_string(), "4".to_string()),
        ("true".to_string(), "5".to_string()),
        ("true".to_string(), "6".to_string()),
        ("false".to_string(), "7".to_string()),
        ("false".to_string(), "8".to_string()),
        ("false".to_string(), "9".to_string()),
        ("false".to_string(), "10".to_string()),
        ("false".to_string(), "11".to_string()),
        ("false".to_string(), "12".to_string()),
        ("false".to_string(), "13".to_string()),
        ("false".to_string(), "14".to_string()),
    ];
    let priority_entry = vec![
        ("0".to_string(), "1".to_string()),
        ("1".to_string(), "2".to_string()),
        ("2".to_string(), "3".to_string()),
        ("2".to_string(), "4".to_string()),
        ("3".to_string(), "5".to_string()),
        ("3".to_string(), "6".to_string()),
        ("0".to_string(), "7".to_string()),
        ("0".to_string(), "8".to_string()),
        ("0".to_string(), "9".to_string()),
        ("0".to_string(), "10".to_string()),
        ("0".to_string(), "11".to_string()),
        ("1".to_string(), "12".to_string()),
        ("3".to_string(), "13".to_string()),
        ("3".to_string(), "14".to_string()),
    ];

    let mut index_entrys = HashMap::new();
    pile_for_indexer(&mut index_entrys, PRIMARY_INDEX_NAME.to_string(), id_entry);
    pile_for_indexer(
        &mut index_entrys,
        "spec.creationTime".to_string(),
        creation_time_entry,
    );
    pile_for_indexer(&mut index_entrys, "spec.top".to_string(), top_entry);
    pile_for_indexer(
        &mut index_entrys,
        "spec.priority".to_string(),
        priority_entry,
    );
    let indexer = MockIndexer::mock_with(index_entrys);
    let view_impl =
        QueryIndexView ::new::<FakeExtension>(Arc::new(RefCell::new(indexer))).unwrap();
    view_impl
}

pub fn pile_for_indexer<T: ExtensionOperator>(
    map: &mut HashMap<String, Arc<RefCell<MockIndexEntry<T>>>>,
    property_name: String,
    entries: Vec<(String, String)>,
) {
    let index_attr = IndexAttributeFactory::simple_attribute(
        "FakeExtension".to_string(),
        |a: &FakeExtension| a.get_metadata().get_name(),
    );
    let spec = IndexSpec::new("".to_string(), OrderType::ASC, true, index_attr);

    let descriptor = IndexDescriptor::new(spec);
    let mut sorted_entries = entries.clone();
    sort_entries(&mut sorted_entries);
    let id_position_map = id_position_map(&sorted_entries);

    let indexed_map = to_key_object_map(&sorted_entries);

    let vec = indexed_map.keys().cloned().collect();

    let index_entry = MockIndexEntry::<T>::mock(entries, id_position_map, vec);

    let index_entry = Arc::new(RefCell::new(index_entry));
    map.insert(property_name, Arc::clone(&index_entry));
}

pub fn id_position_map(entries: &Vec<(String, String)>) -> HashMap<String, i32> {
    let map = to_key_object_map(entries);
    let mut i = 0;
    let mut id_position_map = HashMap::new();
    for (key, value) in map.iter() {
        for id in value {
            id_position_map.insert(id.clone(), i);
        }
        i = i + 1;
    }
    id_position_map
}

pub(crate) fn sort_entries(entries: &mut Vec<(String, String)>) {
    entries.sort_by(|(a_key, a_value), (b_key, b_value)| key_comparator::compare(a_key, b_key))
}

pub(crate) fn to_key_object_map(entries: &Vec<(String, String)>) -> OrderMap<String, Vec<String>> {
    let mut map: OrderMap<String, Vec<String>> = OrderMap::new();
    entries.iter().for_each(|(key, value)| {
        if map.contains_key(key) {
            map.get_mut(key).unwrap().push(value.clone());
        } else {
            map.insert(key.clone(), vec![value.clone()]);
        }
    });
    map
}

pub struct MockIndexEntry<T: ExtensionOperator> {
    _marker_t: std::marker::PhantomData<T>,
    entries: Vec<(String, String)>,
    id_position_map: HashMap<String, i32>,
    indexed_keys: OrderSet<String>,
}

impl<T: ExtensionOperator> MockIndexEntry<T> {
    pub(crate) fn mock(
        entries: Vec<(String, String)>,
        id_position_map: HashMap<String, i32>,
        indexed_keys: OrderSet<String>,
    ) -> Self {
        MockIndexEntry {
            _marker_t: std::marker::PhantomData,
            entries,
            id_position_map,
            indexed_keys,
        }
    }
}

impl<T: ExtensionGVK> IndexEntry for MockIndexEntry<T> {
    type T = T;

    fn add_entry(&mut self, index_keys: Vec<String>, object_key: String) -> AppResult<()> {
        todo!()
    }

    fn remove_entry(&mut self, indexed_key: String, object_key: String) {
        todo!()
    }

    fn remove(&mut self, object_key: String) {
        todo!()
    }

    fn get_index_descriptor(&self) -> &IndexDescriptor<T> {
        todo!()
    }

    fn get_inner_index_descriptor(self) -> IndexDescriptor<Self::T> {
        todo!()
    }

    fn indexed_keys(&self) -> OrderSet<String> {
        todo!()
    }

    fn entries(&self) -> impl Iterator<Item = (&String, &String)> {
        // self.entries.iter()
        let map = self.entries.iter().map(|(a, b)| (a, b));
        map
    }

    fn get_id_position_map(&self) -> HashMap<String, i32> {
        self.id_position_map.clone()
    }

    fn get_object_names_by(&self, index_key: &str) -> Vec<String> {
        let mut vec = self.entries.clone();
        sort_entries(&mut vec);
        let index_map = to_key_object_map(&vec);
        index_map.get(index_key).unwrap().clone()
    }

    fn clear(&mut self) {
        todo!()
    }
}

pub struct MockIndexer<E: ExtensionGVK> {
    pub index_entry: HashMap<String, Arc<RefCell<MockIndexEntry<E>>>>,
}

impl<E: ExtensionGVK> MockIndexer<E> {
    fn mock() -> Self {
        MockIndexer {
            index_entry: Default::default(),
        }
    }
    pub(crate) fn mock_with(index_entry: HashMap<String, Arc<RefCell<MockIndexEntry<E>>>>) -> Self {
        MockIndexer { index_entry }
    }
}

impl<E: ExtensionGVK> Indexer for MockIndexer<E> {
    type Entry<T>   = MockIndexEntry<E>
    where
        T: ExtensionGVK;
    fn index_record<E: ExtensionGVK>(&mut self, extension: E) -> AppResult<()> {
        todo!()
    }

    fn update_record<T: ExtensionGVK>(&mut self, extension: T) -> AppResult<()> {
        todo!()
    }

    fn un_index_record<T: ExtensionGVK>(&mut self, extension_name: &str) -> AppResult<()> {
        todo!()
    }

    fn find_index_by_name<T: ExtensionGVK>(
        &self,
        name: &str,
    ) -> AppResult<Option<&Arc<RefCell<MockIndexEntry<T>>>>> {
        todo!()
    }

    fn create_index_entry<T: ExtensionGVK>(
        &self,
        descriptor: IndexDescriptor<T>,
    ) -> AppResult<MockIndexEntry<T>> {
        todo!()
    }

    fn remove_index_records<F, T: ExtensionGVK>(&mut self, match_fn: F) -> AppResult<()>
    where
        F: Fn(&IndexDescriptor<FakeExtension>) -> bool,
    {
        todo!()
    }

    fn get_index_entry<T: ExtensionGVK>(
        &self,
        property_name: &str,
    ) -> AppResult<Arc<RefCell<MockIndexEntry<T>>>> {
        let option = self.index_entry.get(property_name).unwrap();
        Ok(Arc::clone(option))
    }

    fn ready_indexes_iterator<T: ExtensionGVK>(&self) -> AppResult<Vec<Arc<RefCell<MockIndexEntry<T>>>> >{
        todo!()
    }

    fn all_indexes_iterator<T: ExtensionGVK>(&self) -> AppResult<Vec<Arc<RefCell<MockIndexEntry<T>>>>> {
        todo!()
    }
}

#[gvk(
    group = "test.halo.run",
    version = "v1",
    kind = "FakeExtension",
    singular = "fakeextension",
    plural = "fakes"
)]
pub struct FakeExtension {
    pub email: String,
    pub tags: HashSet<String>,
}

impl std::hash::Hash for FakeExtension {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.metadata.hash(state);
        self.email.hash(state);
        // Skip tags field since HashSet doesn't implement Hash
    }
}
